def _f_accel_decel(t, old_duration, new_duration, abruptness=1.0, soonness=1.0):
    a = 1.0 + abruptness

    def _f(t):
        def f1(t):
            return (0.5) ** (1 - a) * (t ** a)

        def f2(t):
            return 1 - f1(1 - t)

        return (t < 0.5) * f1(t) + (t >= 0.5) * f2(t)

    return old_duration * _f((t / new_duration) ** soonness)


def accel_decel(clip, new_duration=None, abruptness=1.0, soonness=1.0):
    """Accelerates and decelerates a clip, useful for GIF making.

    Parameters
    ----------

    new_duration : float
      Duration for the new transformed clip. If None, will be that of the
      current clip.

    abruptness : float
      Slope shape in the acceleration-deceleration function. It will depend
      on the value of the parameter:

      * ``-1 < abruptness < 0``: speed up, down, up.
      * ``abruptness == 0``: no effect.
      * ``abruptness > 0``: speed down, up, down.

    soonness : float
      For positive abruptness, determines how soon the transformation occurs.
      Should be a positive number.

    Raises
    ------

    ValueError
      When ``sooness`` argument is lower than 0.

    Examples
    --------

    The following graphs show functions generated by different combinations
    of arguments, where the value of the slopes represents the speed of the
    videos generated, being the linear function (in red) a combination that
    does not produce any transformation.

    .. image:: /_static/accel_decel-fx-params.png
       :alt: acced_decel FX parameters combinations
    """
    if new_duration is None:
        new_duration = clip.duration
    if soonness < 0:
        raise ValueError("'sooness' should be a positive number")

    return clip.time_transform(
        lambda t: _f_accel_decel(t, clip.duration, new_duration, abruptness, soonness)
    ).with_duration(new_duration)
